package cn.epsilon3.service.jwtProcessor.impl;

import com.alibaba.fastjson.JSON;
import cn.epsilon3.exception.Epsilon3Exception;
import cn.epsilon3.configBean.InitializingTokenConfig;
import cn.epsilon3.service.jwtProcessor.JwtTokenProcessor;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.Map;

/**
 * token处理器实现类
 *
 * @author epsilon3
 */
public class JwtTokenProcessorImpl implements JwtTokenProcessor {

    @Resource
    InitializingTokenConfig initializingTokenConfig;

    @Override
    public String createToken(String subject,
                              String issuer,
                              String audience,
                              Map<String, Object> claims) {
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "RS256")
                .setSubject(subject)
                .setIssuer(issuer)
                .setAudience(audience)
                .setIssuedAt(new Date())
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + initializingTokenConfig.getAccessTokenExpireTime() * 1000))
                .signWith(SignatureAlgorithm.RS256, getRsaPrivateKey(initializingTokenConfig.getPrivateKey()))
                .compact();
    }

    @Override
    public Claims parseToken(String token) {
        return Jwts.parser().setSigningKey(getRsaPublicKey(initializingTokenConfig.getPublicKey())).parseClaimsJws(token).getBody();
    }

    @Override
    public Object parseToken(String token, String key, Class clazz) {
        Map<String, Object> map = (Map<String, Object>) this.parseToken(token).get(key);
        return JSON.parseObject(JSON.toJSONString(map), clazz);
    }

    @Override
    public Boolean checkToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        try {
            Jwts.parser().setSigningKey(initializingTokenConfig.getTokenSalt()).parseClaimsJws(token);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public RSAPrivateKey getRsaPrivateKey(String privateKey) {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key;
        try {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            throw new Epsilon3Exception("get RSAPrivateKey fail:" + e.getMessage());
        }
        return key;
    }

    @Override
    public RSAPublicKey getRsaPublicKey(String publicKey) {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        RSAPublicKey key;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            key = (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            throw new Epsilon3Exception("get RSAPublicKey fail:" + e.getMessage());
        }
        return key;
    }

}
